using System;
using System.Configuration;
using System.Configuration.Internal;
using System.Xml;

namespace DirectoryWatcher
{
    /// <summary>
    /// Represent a program and its options that should be executed in response to a filesystem 
    /// event.
    /// </summary>
    public class ProgramToExecute : ConfigurationElement
    {
        /// <summary>
        /// Line number in the configuration file for this element (used for possible exception 
        /// messages).
        /// </summary>
        private int lineNumber = 0;
        /// <summary>
        /// Path to the configuration file in which this element resides (used for possible 
        /// exception messages).
        /// </summary>
        private string fileName = "";

        /// <summary>
        /// Path to the program executable.
        /// </summary>
        [ConfigurationProperty("path", DefaultValue = "")]
        public string Path
        {
            get
            {
                return (string)base["path"];
            }
        }

        /// <summary>
        /// Arguments associated with the program that should be passed on the command line; the 
        /// following string substitutions will be made:
        /// <list type="bullet">
        /// <item>
        /// <term>{P}</term>
        /// <description>The full path to the file</description>
        /// </item>
        /// <item>
        /// <term>{F}</term>
        /// <description>The filename of the file</description>
        /// </item>
        /// <item>
        /// <term>{f}</term>
        /// <description>The filename of the file without the extension</description>
        /// </item>
        /// <item>
        /// <term>{E}</term>
        /// <description>The file's extension</description>
        /// </item>
        /// <item>
        /// <term>{D}</term>
        /// <description>The full path to the directory</description>
        /// </item>
        /// </list>
        /// </summary>
        [ConfigurationProperty("arguments", DefaultValue = "")]
        public string Arguments
        {
            get
            {
                return (string)base["arguments"];
            }
        }

        /// <summary>
        /// Flag indicating whether or not the contents of the file should be redirected to the 
        /// process' standard input stream.
        /// </summary>
        [ConfigurationProperty("redirectFileToStdin", DefaultValue = false)]
        public bool RedirectFileToStdin
        {
            get
            {
                return (bool)base["redirectFileToStdin"];
            }
        }

        /// <summary>
        /// Contains the contents of the runtime-compiled code that represents this program.
        /// </summary>
        [ConfigurationProperty("code")]
        public ProgramCode Code
        {
            get
            {
                return (ProgramCode)base["code"];
            }
        }

        /// <summary>
        /// Instantiates the object using data stored in XML; records the filename and line number
        /// (for use later in possible exceptions) and then calls the base method.
        /// </summary>
        /// <param name="reader">
        /// Reader object that is involved in the deserialization.
        /// </param>
        /// <param name="serializeCollectionKey">
        /// True to serialize only the collection key properties, false otherwise.
        /// </param>
        protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
        {
            lineNumber = ((IConfigErrorInfo)reader).LineNumber;
            fileName = ((IConfigErrorInfo)reader).Filename;

            base.DeserializeElement(reader, serializeCollectionKey);
        }

        /// <summary>
        /// Called after the deserialization process is complete; validates the object's data.
        /// </summary>
        protected override void PostDeserialize()
        {
            if (Path != "" && Code.Text != null)
                throw new ConfigurationErrorsException("A program to excecute cannot have both an executable path and runtime-compiled code.", fileName, lineNumber);

            if (Path == "" && Code.Text == null)
                throw new ConfigurationErrorsException("A program to excecute must have either an executable path or runtime-compiled code.", fileName, lineNumber);

            base.PostDeserialize();
        }
    }
}
